package org.orr.budgetpro.controller;

import java.security.Principal;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import org.orr.budgetpro.entities.Budget;
import org.orr.budgetpro.entities.ExpenseItem;
import org.orr.budgetpro.entities.IncomeItem;
import org.orr.budgetpro.entities.User;
import org.orr.budgetpro.services.BudgetCalculator;
import org.orr.budgetpro.services.BudgetService;
import org.orr.budgetpro.services.ExpenseCategoryService;
import org.orr.budgetpro.services.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/manage")
public class ManageController {
	static final String USER_MESSAGES_KEY = "userMessages";

	@Autowired
	UserService userService;

	@Autowired
	BudgetService budgetService;

	@Autowired
	BudgetCalculator budgetCalculator;

	@Autowired
	ExpenseCategoryService categoryService;

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public ModelAndView getManagePage(HttpServletRequest request,
			Principal principal) {
		ModelAndView modelView = new ModelAndView();

		User user = getCurrentUser(principal);
		Budget budget = budgetService.getBudgetByUserId(user.getUserId());

		modelView.addObject("budget", budget);
		modelView.addObject("incomeSubtotal",
				budgetCalculator.getIncomeSubtotal(budget));
		modelView.addObject("expenseSubtotal",
				budgetCalculator.getExpenseSubtotal(budget));

		if (budget.getIncomeItems().size() == 0) {
			// Add empty rows to get user started
			List<IncomeItem> income = new ArrayList<IncomeItem>();
			income.add(new IncomeItem(null, "", null));
			budget.setIncomeItems(income);
		}

		if (budget.getExpenseItems().size() == 0) {
			// Add empty rows to get user started
			List<ExpenseItem> expense = new ArrayList<ExpenseItem>();
			expense.add(new ExpenseItem(null, "", null, null));
			budget.setExpenseItems(expense);
		}

		loadReferenceData(modelView);

		addUserMessagesFromRequest(modelView, request);

		modelView.setViewName("manage");
		return modelView;
	}

	@RequestMapping(value = "/", method = RequestMethod.POST)
	public ModelAndView saveBudget(
			@ModelAttribute("budget") @Valid Budget budget,
			BindingResult result, Principal principal) {
		ModelAndView modelView = new ModelAndView();

		if (result.hasErrors()) {
			addUserMessage(modelView,
					"There are errors in your budget; changes have not been saved!");

			loadReferenceData(modelView);

			modelView.setViewName("manage");
			return modelView;
		}

		User user = getCurrentUser(principal);

		// Make sure budgetId belongs to for the current user
		if (!budgetService.userOwnsBudget(budget.getBudgetId(),
				user.getUserId())) {
			addUserMessage(modelView,
					"Error: you are trying to modify someone else's budget; no changes saved.");

			loadReferenceData(modelView);

			modelView.setViewName("manage");
			return modelView;
		}

		budget.setUser(user);
		user.setBudget(budget);

		List<IncomeItem> incomeItems = budget.getIncomeItems();
		for (IncomeItem item : incomeItems) {
			item.setBudget(budget);
		}

		List<ExpenseItem> expenseItems = budget.getExpenseItems();
		for (ExpenseItem item : expenseItems) {
			item.setBudget(budget);
			item.setExpenseCategory(categoryService.getExpenseCategory(item
					.getExpenseCategory().getCategoryId()));
		}

		budgetService.saveBudget(budget);

		addUserMessage(modelView, "Your changes have been saved.");

		modelView.setViewName("redirect:/secure/manage/");

		return modelView;
	}

	@RequestMapping(value = "/incomeTotal", method = RequestMethod.POST)
	public @ResponseBody
	Integer getIncomeTotal(@ModelAttribute("budget") Budget budget) {
		return budgetCalculator.getIncomeSubtotal(budget);
	}

	@RequestMapping(value = "/expenseTotal", method = RequestMethod.POST)
	public @ResponseBody
	Integer getExpenseTotal(@ModelAttribute("budget") Budget budget) {
		return budgetCalculator.getExpenseSubtotal(budget);
	}

	@RequestMapping(value = "/deleteIncomeItem", method = RequestMethod.GET)
	public @ResponseBody
	void deleteIncomeItem(Integer incomeItemId) {
		budgetService.deleteIncomeItem(incomeItemId);
	}

	@RequestMapping(value = "/deleteExpenseItem", method = RequestMethod.GET)
	public @ResponseBody
	void deleteExpenseItem(Integer expenseItemId) {
		budgetService.deleteExpenseItem(expenseItemId);
	}

	protected void loadReferenceData(ModelAndView modelView) {
		modelView.addObject("expenseCategories",
				categoryService.getExpenseCategories());
	}

	protected User getCurrentUser(Principal principal) {
		User user = userService.getUser(principal.getName());

		if (user == null) {
			// If user isn't found, initialize a user and budget, and persist

			user = new User();
			user.setUserId(principal.getName());
			// In the future, this could be expanded to actually register the
			// user and get firstname, lastname, etc

			Budget budget = new Budget();
			budget.setUser(user);

			user.setBudget(budget);

			userService.saveUser(user);
			budgetService.saveBudget(budget);
		}

		return user;
	}

	private void addUserMessage(ModelAndView modelView, String message) {
		List<String> userMessages = (List<String>) modelView.getModelMap().get(
				USER_MESSAGES_KEY);

		if (userMessages == null) {
			userMessages = new ArrayList<String>();
			modelView.getModelMap().put(USER_MESSAGES_KEY, userMessages);
		}

		userMessages.add(message);
	}

	private void addUserMessagesFromRequest(ModelAndView modelView,
			HttpServletRequest request) {
		String[] requestMessages = request
				.getParameterValues(USER_MESSAGES_KEY);
		if (requestMessages != null) {
			for (String message : requestMessages) {
				addUserMessage(modelView, message);
			}
		}

	}
}
